/*
 *   Copyright (C) 2007 by Andrzej Oczkowicz
 *   andrew_lz@poczta.fm
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 *   KMediaCatalog :)
 *
 */
#include <QtGui>
#include <QtUiTools>
#include "dbg.h"
#include "kmaster.h"
#include "ksearch.h"
#include "kutils.h"
#include "kmodmgr.h"


void scanThread::run()
{
    _do_term = false;
    if (!ksearch) {
        //Master not set
        error(ERR_NULL_POINTER, "searching master class is not set!");
        return;
    }
    _D << "scan thread run!!";
    ksearch->data.done = false;
    ksearch->findAllFiles(ksearch->data.searchRoot, _do_term);
    ksearch->onDir(ksearch->data.searchRoot, _do_term);
    ksearch->data.done = true;
    _D << "scan thread end: " << ksearch->data.done;
}

bool scanThread::stop(int waittime)
{
    if ( _do_term ) {
        return ! isRunning();
    }
    _do_term = true;
    wait(waittime);
    _D << "scan thread stop";
    return ! isRunning();
}

scanThread::scanThread(kSearch * owner)
{
  ksearch = owner;
}

kSearch::kSearch(QObject *parent)
 : QObject(parent)
{
  data.terminated = false;
  data.writing = false;
  data.done = false;
  data.terminated = false;
  data.thread = 0;
  data.master = kMaster::getInstance();
  data.current = QString::null;
  data.searchRoot = QString::null;
  memset(&data.stats, 0, sizeof(data.stats));
  mgr = kMaster::getInstance()->modMgr();
}


kSearch::~kSearch()
{
}

void kSearch::showFile(QString & file, bool is_dir)
{
  Q_UNUSED(file);
  Q_UNUSED(is_dir);
}

int kSearch::scanDirectory(const QString & dir)
{
    scanThread *thread = new scanThread(this);
    dlgSearch   = new dkSearch(data);
    data.searchRoot = dir;
    data.thread = thread;
    memset(&data.stats, 0, sizeof(data.stats));
//     // run in current thread
//     thread->run();
    thread->start();
    int retcode = dlgSearch->exec();
    thread->stop();
    return retcode;
}

#define TERMINATE   if (terminated) return 2;
int kSearch::onDir(QString & dir, bool & terminated)
{
    TERMINATE
    kUtils::path(dir);
    QDir dr(dir);
    if ( ! dr.isReadable() ) {
        showFile(dir, true);
        return error(ERR_ACCESS/*, QString("Access error on &1").arg(dir).toChar */);
    }
    dr.setFilter( QDir::Dirs | QDir::Hidden );
    QFileInfoList list(dr.entryInfoList());
    QFileInfo *fi;
    for (int i=0; i < list.size(); i++ ){
      fi = &list[i];
        TERMINATE
        if ( fi->fileName() != "."  && fi->fileName() != ".." && ! fi->isSymLink() ) {
            if ( ! fi->isDir() )
                continue;
            QString s(dir);
            s.append( fi->fileName() );
            if (fi->isSymLink())
                data.stats.links++;
            kUtils::path(s);
            data.stats.dirs++;
            showFile(s, true);
            processFile(fi);
            onDir(s, terminated);
            findAllFiles(s, terminated);
        }
    }
    return error(ERR_OK);
}

int kSearch::processFile(QFileInfo * fi)
{
  Q_UNUSED(fi); 
  return mgr->processFile(fi);
}

void kSearch::findAllFiles(QString & dir, bool & terminated)
{
    QDir files(dir);
    if (!files.isReadable()) {
        _W << "Error reading: " << dir.toLocal8Bit();
        return;
    }
    files.setFilter( QDir::Files | QDir::Hidden | QDir::System );
    QFileInfoList list(files.entryInfoList());
    data.stats.files += list.count();
    QFileInfo *fi;
    for (int i=0; i < list.size(); i++) {
        fi = &list[i];
        if (terminated)
            return;
        data.writing = true;
        data.current = kUtils::path(dir);
        data.writing = false;
        data.current.append(fi->fileName());
        if (fi->isSymLink())
            data.stats.links++;
        showFile(data.current);
        processFile(fi);
    }
}

int kSearch::scAdd(QString & file)
{
    Q_UNUSED(file); return 0;
}


/*  */
dkSearch::dkSearch(kSearch::scanOptions & data)
:QDialog( kMaster::getInstance()->mainWindowDialog() )
{
  scanData = &data;
  statTimer = new QTimer(this);
  connect(statTimer, SIGNAL( timeout() ), this, SLOT( statTimer_timeout() ));
  setupUi(this);
  kUtils::makeTopPicture(topFrame, tr("<b>SCANNING...</b>"), "right_scan.png");
  setButtonType();
  start.start();
  statTimer->start(100);
}

dkSearch::~dkSearch()
{
}

void dkSearch::statTimer_timeout()
{
  updateStats();
  if ( scanData->done )
    setButtonType(false);
}

void dkSearch::on_buttonOk_clicked(bool checked)
{
  Q_UNUSED(checked);
  if ( ! scanData->done ) {
    if ( kUtils::appQuery( tr("Cancel scanning ?")) ) {
      if ( ! scanData->thread->stop( 5000 ) ){ //wait 5 secs
        if ( kUtils::appQuery(tr("Scanning thread not responding. Terminate it?\nWarning!!!\nTerminating thread may krash application!")) )
          while ( scanData->thread->isRunning() )
            scanData->thread->terminate();
      } else {
        statTimer->stop();
        done(1);
      }
    }
  } else {
    statTimer->stop();
    done(0);
  }
}

void dkSearch::setButtonType(bool cancel)
{
  if (cancel){
    buttonOk->setText( tr("&Cancel") );
    progressBar->setValue(0);
    progressBar->setMaximum(0);
    progressBar->setMinimum(0);
  } else {
    buttonOk->setText( tr("&Ok") );
    progressBar->setMaximum(100);
    progressBar->setValue(100);
    statTimer->stop();
  }
}

void dkSearch::updateStats()
{
#define SET_NUM(IDX, NUM) labelScan##IDX->setText( QString("%1").arg(NUM) );
  int secs = start.elapsed()/1000;
  QString t;
  t.sprintf( "%.2d:%.2d:%.2d", secs/3600, (secs%3600)/60, (secs%3600)%60 );
  labelScan0->setText(t);
  if (scanData){
    if (!scanData->writing){
      labelStatus->setText(scanData->current);
      SET_NUM(1, scanData->stats.files);
      SET_NUM(2, scanData->stats.dirs);
    }
  } else _E << "scanData is NULL :/";
}

void dkSearch::closeEvent(QCloseEvent *e)
{
  e->ignore();
  on_buttonOk_clicked();
}
